package com.zzjson.netty.rpc.consumer.proxy;

import com.zzjson.netty.rpc.protocol.InvokerProtocol;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * <p>****************************************************************************</p>
 * <p><b>Copyright © 2010-2020 shuncom team All Rights Reserved<b></p>
 * <ul style="margin:15px;">
 * <li>Description : com.zzjson.netty.rpc.consumer.proxy</li>
 * <li>Version     : 1.0.0</li>
 * <li>Creation    : 2020年03月03日</li>
 * <li>@author     : zzy0_0</li>
 * </ul>
 * <p>****************************************************************************</p>
 */
public class RpcProxy {
	public static <T> T create(Class<?> clazz) {
		MethodProxy proxy = new MethodProxy(clazz);
		T result = (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, proxy);
		return result;
	}

	private static class MethodProxy implements InvocationHandler {
		private Class<?> clazz;

		public MethodProxy(Class<?> clazz) {
			this.clazz = clazz;
		}

		@Override
		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
			if (Object.class.equals(method.getDeclaringClass())) {
				return method.invoke(this, args);
			} else {
				return rpcInvoker(proxy, method, args);
			}
		}

		private Object rpcInvoker(Object proxy, Method method, Object[] args) {

			RpcProxyHandler rpcProxyHandler = new RpcProxyHandler();
			InvokerProtocol msg = new InvokerProtocol();
			msg.setClassName(this.clazz.getName());

			msg.setMethodName(method.getName());
			msg.setParams(method.getParameterTypes());
			msg.setValues(args);

			//发起网络请求
			NioEventLoopGroup workGroup = new NioEventLoopGroup();

			Bootstrap client = new Bootstrap();
			client.group(workGroup).channel(NioSocketChannel.class).
					option(ChannelOption.TCP_NODELAY, true).
					handler(new ChannelInitializer<SocketChannel>() {
						@Override
						protected void initChannel(SocketChannel ch) throws Exception {
							//	在 Netty中把所有的业务逻辑处理 全部归总到了一个队列中
							//	这个队列中不包含了所有的处理逻辑，这些处理逻辑netty中有一个封装
							//	无锁化串行任务队列
							//	pipline
							ChannelPipeline pipeline = ch.pipeline();


							//	对于自定义协议的内容要进行编解码
							//解码:最大长度底层使用int的缓冲区来接受,信息头，每一个字段的长度（int是占四个字节）,添加字段的补偿值，从解码帧中去除的第一个字节数（一般用一个单位长度去套）
							pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));

							//自定义编码器，int字段长度是4
							pipeline.addLast(new LengthFieldPrepender(4));

							//	实参处理
							pipeline.addLast("encoder", new ObjectEncoder());

							pipeline.addLast("decoder", new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));

							//前面编解码是对数据的解析

							//最后一步是属于自己的逻辑
							//1.注册，对传过来的对象进行注册,给每一个对象起一个名字（名字是对外提供服务的名字）
							//2.服务位置登记
							//定义类执行的逻辑
							pipeline.addLast(rpcProxyHandler);
						}
					});
			try {
				ChannelFuture future = client.connect("127.0.0.1", 8080).sync();
				future.channel().writeAndFlush(msg).sync();

				future.channel().closeFuture().sync();

			} catch (InterruptedException e) {
				e.printStackTrace();
			} finally {

				workGroup.shutdownGracefully();
			}


			return rpcProxyHandler.getResponse();
		}
	}

}
